#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include "protocol.hpp"

using namespace std;

namespace client
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        CONNECT_ERR
    };

#define NUM 1024

    class TcpClient
    {
    public:
        TcpClient(const string &serverip, uint16_t &serverport)
            : _sock(-1), _serverip(serverip), _serverport(serverport)
        {
        }

        ~TcpClient()
        {
            if (_sock >= 0)
                close(_sock);
        }

        void initClient()
        {
            // 1. 创建socket
            _sock = socket(AF_INET, SOCK_STREAM, 0);
            if (_sock < 0)
            {
                cerr << "create socket err!" << endl;
                exit(SOCKET_ERR);
            }

            // 2. tcp的客户端要不要bind？要的！ 要不要显式的bind？不要！这里尤其是client port要让OS自动随机指定！
            // 3. 要不要listen？不要！
            // 4. 要不要accept? 不要！
            // 5. 要什么呢？？要发起链接！
        }

        void start()
        {
            // 2. 发起链接
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(_serverport);
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());
            if (connect(_sock, (struct sockaddr *)&server, sizeof(server)) < 0)
            {
                cerr << "connect error!" << endl;
                exit(CONNECT_ERR);
            }

            while (true)
            {
                cout << "mycal>>> ";

                string line;
                getline(cin, line);

                request req;
                // 分割终端输入
                req = ParseLine(line);

                // cout << "x: " << req._x << " y: " << req._y << " op : " << req._op << endl;

                // request req(10, 10, '+');

                // 先进行序列化
                string content;
                req.serialize(&content);

                // 再进行添加报头
                string text = enLength(content);

                // 发送报文
                send(_sock, text.c_str(), text.size(), 0);
                // send(_sock, text.c_str(), text.size(), 0);
                // send(_sock, text.c_str(), text.size(), 0);
                // send(_sock, text.c_str(), text.size(), 0);
                // send(_sock, text.c_str(), text.size(), 0);

                // 接收报文
                string inbuffer, recv_text;
                if (!recvText(_sock, inbuffer, &recv_text))
                    continue;

                // 去除报头
                string recv_content;
                if (!deLength(recv_text, &recv_content))
                    continue;
                cout << "去除报头后是： " << recv_content << endl;

                // 反序列化
                response resp;
                resp.deserialize(recv_content);

                // 打印结果
                if(resp._exitcode == 0)
                    cout << " [" << resp._msg << "] " << resp._result << endl;
                else
                    cout << resp._exitcode << " [" << resp._msg << "] " << endl;
            }
        }

        request ParseLine(const string &line)
        {
            int n = line.size();
            int i = 0;

            string left;
            string right;
            char op;

            // 判断第一个数是不是负数
            if (line[0] == '-')
            {
                left += "-";
                i++;
            }

            int status = 0;
            // 0 表示 操作符之前， 1 表示遇到操作符， 2 表示操作符之后

            while (i < n)
            {
                switch (status)
                {
                case 0:
                {
                    if (isdigit(line[i]))
                        left += line[i++];
                    else
                    {
                        op = line[i];
                        status = 1;
                    }
                }
                break;
                case 1:
                {
                    i++;
                    // 判断第二个数是不是负数
                    if (line[i] == '-')
                        right += "-";
                    status = 2;
                }
                break;
                case 2:
                {
                    if (isdigit(line[i]))
                        right += line[i++];
                }
                break;
                default:
                    break;
                }
            }
            return request(atoi(left.c_str()), atoi(right.c_str()), op);
        }

    private:
        int _sock;
        string _serverip;
        uint16_t _serverport;
    };

} // namespace client
